﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Timers;

namespace LcfContols.asyncMessage
{
    /// <summary>
    /// 消息处理类
    /// </summary>
    public static class MessageInvoke
    {
        #region 公共变量
        /// <summary>
        /// 用于存放处理中的消息回调函数
        /// </summary>
        private static Dictionary<string, Delegate> callbackDict = new Dictionary<string, Delegate>();
        #endregion

        #region 本地数据处理变量
        /// <summary>
        /// 请求的消息队列
        /// </summary>
        private static Queue<MsgReport> queue = new Queue<MsgReport>();

        /// <summary>
        /// 有消息进入队列触发事件
        /// </summary>
        private static AutoResetEvent queueAddEvent;

        /// <summary>
        /// 处理消息的线程
        /// </summary>
        private static Thread[] threads;

        /// <summary>
        /// 线程是否开启
        /// </summary>
        public static bool close = true;

        /// <summary>
        /// 定时触发队列消息以免队列假死
        /// </summary>
        private static System.Timers.Timer ter = new System.Timers.Timer();

        /// <summary>
        /// 锁对象
        /// </summary>
        private static object lockobj = new object();
        #endregion

        /// <summary>
        /// 获取数据请求
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="callback"></param>
        /// <param name="args"></param>

        public static void Get(object obj, string method, Delegate callback, params object[] args)
        {
            MsgReport msg = new MsgReport();
            msg.args = args;
            msg.methodName = method;
            msg.obj = obj;
            msg.key = CallbackAdd(callback);
            if (close)
                StartTask();
            lock (lockobj)
            {
                queue.Enqueue(msg);
            }
            queueAddEvent.Set();
        }

        /// <summary>
        /// 开启消息接受线程
        /// </summary>
        public static void StartTask()
        {
            close = false;
            queueAddEvent = new AutoResetEvent(false);
            int count = SystemUtils.NumberOfLogicalProcessors();
            threads = new Thread[count];
            for (int i = 0; i < count; i++)
            {
                threads[i] = new Thread(DoTask);
                threads[i].Name = "本地数据处理线程[" + i + "]";
                threads[i].Start();
            }
            ter.Elapsed += Ter_Elapsed;
            ter.Enabled = true;
            ter.Interval = 3000;
        }

        /// <summary>
        /// 定时触发队列事件,防止队列假死
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Ter_Elapsed(object sender, ElapsedEventArgs e)
        {
#if DEBUG
            Console.WriteLine("定时触发队列事件");
#endif
            queueAddEvent.Set();
        }

        /// <summary>
        /// 防止跨线程操作control
        /// </summary>
        public static void TargetControl(Delegate callback, params object[] args)
        {
            if (callback.Target is Control) //判断代理实例是否是窗口，因为窗口更新UI会出现线程异常。要用Invoke执行。这里就统一进行Invoke代理
            {                                           //这样在callback方法中就无需检测线程问题
                Control f = (Control)callback.Target;
                if (f.InvokeRequired){
                    f.Invoke(callback, args);
                    return;
                }
            }
            callback.Method.Invoke(callback.Target, args);
        }

        /// <summary>
        /// 请求消息处理
        /// </summary>
        public static void DoTask()
        {
            MsgReport msg = null;
            //任务执行循环
            while (!close)
            {
                queueAddEvent.WaitOne();
                if ((close || queue.Count < 1) && msg == null)
                { //如果是消息关闭时发出来的事件，直接返回退出 ,如果msg还没处理完成的,则不退出
                    continue;
                }
                try
                {
                    if (msg == null)
                    {
                        lock (lockobj)
                        { //队列防止
                            msg = queue.Dequeue();
                            if (queue.Count > 0) //队列中还有消息则继续出发添加事件
                                queueAddEvent.Set();
                        }
                    }
                    /*Thread.Sleep(1000);*/
                    if (msg == null)
                        continue;
                    object o = msg.obj;
                    if (o == null)
                        continue;
                    MethodInfo m = o.GetType().GetMethod(msg.methodName);
                    try
                    {
                        object result = m.Invoke(o, msg.args);
                        if (callbackDict.ContainsKey(msg.key))
                        {
                            Delegate callback = callbackDict[msg.key];
                            callbackDict.Remove(msg.key);
                            MethodInfo mm = callback.Method;
                            //得到回调函数的参数，如果一个参数直接回调结果，否则方法参数通过第二个参数回传
                            object[] args = mm.GetParameters().Length > 1 ? new object[] { result, msg.args } : new object[] { result };
                            try
                            {
                                TargetControl(callback, args);
                            }
                            catch (Exception ex)
                            {
#if DEBUG
                                Console.WriteLine(ex);
#endif
                            }
                            msg = null;
                        }
                    }
                    catch (Exception)
                    {
                        callbackDict.Remove(msg.key);
                        msg = null;
                        continue;
                    }
                }
                catch(Exception)
                {
#if DEBUG
                    Console.WriteLine("队列为空");
#endif
                }
            }
        }

        /// <summary>
        /// 关闭消息接受线程
        /// </summary>
        public static void CloseTask()
        {
            ter.Close();
            if (close) //如果线程没有开启，直接返回
                return;
            int length = threads.Length;
            for (int i = 0; i < length; i++)
            {
                queueAddEvent.Set();
                Thread.Sleep(1); //给其他线程一个执行的机会
            }
            Thread.Sleep(10);
            foreach (Thread d in threads)
                d.Abort();
            queueAddEvent.Close(); //关闭线程事件
        }

        /// <summary>
        /// 将回调函数存入字典，数据返回后通过key来处理回调
        /// </summary>
        /// <param name="callback"></param>
        /// <returns></returns>
        private static string CallbackAdd(Delegate callback)
        {
            string key = Guid.NewGuid().ToString("N");
            while (callbackDict.ContainsKey(key))
            {
                key = Guid.NewGuid().ToString("N");
            }
            callbackDict.Add(key, callback);
            return key;
        }
    }

    /// <summary>
    /// 消息请求
    /// </summary>
    public class MsgReport
    {
        /// <summary>
        /// 回调函数标记
        /// </summary>
        public string key { get; set; }

        /// <summary>
        /// 处理方法的名称
        /// </summary>
        public string methodName { get; set; }

        /// <summary>
        /// 方法处理的对象
        /// </summary>
        public object obj { get; set; }

        /// <summary>
        /// 方法参数
        /// </summary>
        public object[] args { get; set; }

    }

}
